<?php

	require_once(dirname(__FILE__).'/class.adminview.php');

	class AKB_CATEGORY extends AdminView
	{
		var $catsById = array();
		var $associations = array();
		var $assocsByQ = array();
		var $questionCount = array();
		var $tree = null;
		var $catsCached = false;
		var $assocsCached = false;

		function AKB_CATEGORY()
		{
			$this->tree = new Tree();
		}

		/**
		* HandlePage
		*
		* Handles page routing based on the current ToDo get string
		* @return void
		*/
		function HandlePage()
		{
			$GLOBALS['Message'] = '';

			if (isset($_GET['ToDo'])) {
				switch (strtolower($_GET['ToDo'])) {
					case 'viewcategories':
					default:
						if ($_SESSION['user']->verify('category', 'edit')
						|| $_SESSION['user']->verify('category', 'delete')) {
							if (isset($_POST['bulkaction'])) {
								$this->BulkAction();
							}
							$this->ShowCategoryList();
						} else {
							$this->NoPermission();
						}
				}
			}
		}

		/**
		* BulkAction
		*
		* The handler for performing an action to multiple items
		* @return void
		*/
		function BulkAction()
		{
			if (!isset($_POST['bulkaction'])) {
				return false;
			}

			if (!isset($_POST['item']) || !is_array($_POST['item'])) {
				return false;
			}

			if (empty($_POST['item'])) {
				return false;
			}
			$g = new API_CATEGORY();
			switch (strtolower($_POST['bulkaction'])) {
				case 'delete':
					if ($_SESSION['user']->verify('category', 'delete')) {
						if ($g->multiDelete($_POST['item'])) {
							$query = "DELETE FROM ".$GLOBALS['tablePrefix']."categoryassociations
							WHERE categoryid IN (".implode(',', array_keys($_POST['item'])).")";
							$GLOBALS['AKB_DB']->Query($query);
							$query = "DELETE FROM ".$GLOBALS['tablePrefix']."catassoc_history
							WHERE categoryid IN (".implode(',', array_keys($_POST['item'])).")";
							$GLOBALS['AKB_DB']->Query($query);
							$GLOBALS['Message'] = MessageBox(GetLang('CategoryMultiDeleteOk'), MSG_SUCCESS);
						} else {
							$GLOBALS['Message'] = MessageBox(GetLang('CategoryMultiDeleteFail').' '.$g->error, MSG_ERROR);
						}
					} else {
						$this->NoPermission();
						return false;
					}
				break;
			}
		}
		/**
		* GetCategoryOptions
		* Get an html options box with categories in it. Categories which are pre
		* selected can be specified as can the format of the html
		*
		* @param array $SelectedCats The cats to pre select in the list
		* @param string $Container The html to use for the option
		* @param string $sel The html to use to signify a cat is selected
		* @param string $Divider The text to prefix sub cats with
		* @param bool $IncludeEmpty Add an option at the top for "
		* please select a category"
		* @param array $hide If not empty then hide catids in this array
		*
		* @return string The html for the options
		*/
		function GetCategoryOptions($SelectedCats, $Container = "<option %s value='%d'>%s</option>", $Sel = "selected=\"selected\"", $Divider = "- ", $IncludeEmpty = true, $workflow = false)
		{
			// Get a list of categories as <option> tags
			$cats = '';

			// Make sure $SelectedCats is an array
			if (!is_array($SelectedCats)) {
				$SelectedCats = array();
			}

			if (empty($SelectedCats) || in_array("0", $SelectedCats)) {
				$sel = "SELECTED";
			} else {
				$sel = "";
			}

			// Do we include the no parent category item in the list ?
			if ($IncludeEmpty) {
				$cats = sprintf("<option %s value='0'>-- %s --</option>\n", $sel, GetLang("NoParent"));
			}

			// Get a formatted list of all the categories in the system
			$categories = $this->getCats($Divider);

			// Work out which cats we want to keep in the list
			$this->getCatsInfo();

			$permitted = $this->GetPermittedCats();
			if ($workflow === true) {
				$permitted = $this->GetPermittedApprovalCats();
			}

			if (!empty($permitted)) {
				$cats_to_keep = $permitted;
				foreach ($permitted as $catid) {
					$thisbranch = $this->tree->GetBranchTo((int) $catid);

					if (is_array($thisbranch)) {
						$cats_to_keep = array_merge($cats_to_keep, $thisbranch);
					}
				}
			} else {
				// There are no restrictions on which cats we can see
				$cats_to_keep = array_keys($categories);
			}

			foreach ($categories as $cid => $cname) {
				if (!in_array($cid, $cats_to_keep)) {
					if (in_array($cid, $SelectedCats)) {
						$s = $Sel . ' class="cat_selected" ';
					} else {
						$s = ' disabled="disabled" class="cat_disabled" ';
						continue;
					}

					$cats .= sprintf($Container, 'onclick="return false;" ' . $s , $cid, $cname);
					continue;
				}

				if (!empty($permitted) && !in_array($cid, $permitted) && $_SESSION['user']->userid != 1) {
					// if there are category restrictions in place
					// and this category isn't one that we are allowed access to
					// and we aren't the super user
					continue;
					$cats .= sprintf($Container, 'disabled="disabled" ', $cid, $cname);
				} else if (empty($permitted) || in_array($cid, $permitted) || $_SESSION['user']->userid == 1) {
					if (in_array($cid, $SelectedCats)) {
						$s = $Sel;
					} else {
						$s = '';
					}
					$cats .= sprintf($Container, $s, $cid, $cname);
				}
			}

			return $cats;
		}

		/**
		* GetParentidSelectOptions
		* Get the options for a select field for chosing the parentid of a cat
		*
		* @param integer $catid The category id to get the list for
		*
		* @return string The html for the options
		*/
		function GetParentSelectOptions($catid=0)
		{
			$this->GetCatsInfo();

			$html = '';

			// Add the root node as an option
			$GLOBALS['OptionValue'] = 0;
			$GLOBALS['OptionText'] = '-- '.GetLang('NoParent').' --';
			if (isset($this->catsById[$catid]) && $this->catsById[$catid]['parentid'] > 0) {
				$GLOBALS['OptionSelected'] = '';
			} else {
				$GLOBALS['OptionSelected'] = ' selected';
			}
			$html .= $GLOBALS['AKB_CLASS_TEMPLATE']->GetSnippet('Option');

			$permitted = $this->GetPermittedCats();

			$upcats = array();
			foreach ($permitted as $cid) {
				$branchto = $this->tree->GetBranchTo((int) $cid);

				if (is_array($branchto)) {
					$upcats = array_merge($upcats, array_diff($branchto, $upcats));
				}
			}

			$subcats = $this->tree->GetBranchFrom($catid, false);

			// We can't make a cat a child of itself
			$subcats[] = $catid;

			// Work out which categories are actually in our list of categories
			// to show
			$cats_to_show = array_diff($upcats, $subcats);

			if (is_array($cats_to_show)) {
				foreach ($cats_to_show as $cid) {
					if (!isset($this->catsById[$cid])) {
						continue;
					}
					$GLOBALS['OptionValue'] = $cid;

					// Work out indeting
					$prefix = str_repeat('&nbsp;', $this->tree->GetDepth($cid) * 4);
					if (!empty($prefix)) {
						$prefix .= '- ';
					}

					$GLOBALS['OptionText'] = $prefix.strip_tags($this->catsById[$cid]['name']);
					if ($this->catsById[$catid]['parentid'] == $cid) {
						$GLOBALS['OptionSelected'] = ' selected';
					} else {
						$GLOBALS['OptionSelected'] = '';
					}
					$html .= $GLOBALS['AKB_CLASS_TEMPLATE']->GetSnippet('Option');
				}
			}

			return $html;
		}

		/**
		* getCatsInfo
		* Get all the information for the categories and save them in the arrays
		* $this->catsById to signify what each of them
		* is indexed by. All functions accessing categories should check to see
		* if one of these arrays already has values and if its empty, call this
		* function to populate it. This allows the arrays to serve as a cache
		* ensuring that the database isn't hit excessively for info on cats
		*
		* @return void
		*/
		function getCatsInfo()
		{
			if ($this->catsCached) {
				return;
			}

			$this->tree = new Tree();

			$query = "SELECT *
				FROM ".$GLOBALS['tablePrefix']."categories
				ORDER BY catorder, name";

			$result = $GLOBALS['AKB_DB']->Query($query);
			while ($row = $GLOBALS['AKB_DB']->Fetch($result)) {
				$this->catsById[$row['categoryid']] = $row;
				$this->tree->nodesByPid[(int) $row['parentid']][] = (int) $row['categoryid'];
			}

			$this->catsCached = true;
		}

		/**
		* getCats
		* Returns an array of categories, each indented and prefixed depending
		* on it's position in the category structure. This function calls itself
		* recursively.
		*
		* @param string $stalk What to prefix a question with to signify it is
		* a descendant of its parent
		* @param int $parentid The category id to get descdendants of
		* @param string $prefix This string grows with whitespace depending on
		* the depth of the item in the tree
		*
		* @return array The formatted array of categories
		*/
		function getCats($stalk = '`- ', $parentid=0, $prefix='')
		{
			$subs = array();
			$formatted = array();

			// If we don't have any data get it from the db
			$this->getCatsInfo();
			if (empty($this->tree->nodesByPid)) {
				$parentid = 0;
			}

			if (!isset($this->tree->nodesByPid[$parentid])) {
				return $formatted;
			}

			// Create the formatted array
			foreach ($this->tree->nodesByPid[$parentid] as $k => $catid) {
				$cat = $this->catsById[$catid];
				if (!empty($prefix)) {
					$formatted[$cat['categoryid']] = $prefix.$stalk.htmlspecialchars($cat['name'], ENT_QUOTES, $GLOBALS['charset']);
				} else {
					$formatted[$cat['categoryid']] = $prefix.htmlspecialchars($cat['name'], ENT_QUOTES, $GLOBALS['charset']);
				}
				$subs = $this->getCats($stalk, $cat['categoryid'], $prefix.'&nbsp;&nbsp;&nbsp;&nbsp;');
				$formatted = $formatted + $subs;
			}

			return $formatted;
		}

		/**
		* getAssocInfo
		* Get the details of category assocations for the given question ids
		*
		* @param array $qids An array of question ids to get the associations for
		*
		* @return void
		*/
		function getAssocInfo($qids)
		{
			if ($this->assocsCached) {
				return;
			}

			$query = "SELECT *
			FROM ".$GLOBALS['tablePrefix']."categoryassociations\n";

			if (!empty($qids)) {
				$query .= "WHERE questionid IN (".implode(',', $qids).")\n";
			}

			$query .= "ORDER BY categoryid";
			$result = $GLOBALS['AKB_DB']->Query($query);
			while ($row = $GLOBALS['AKB_DB']->Fetch($result)) {
				$this->associations[$row['categoryid']][] = $row['questionid'];
				$this->assocsByQ[$row['questionid']][] = $row['categoryid'];
			}
			$this->assocsCached = true;
		}

		/**
		* GetCatPathArray
		* Get the path to a category
		*
		* @param int $catid The id to get the path to
		* @param Boolean $showApprovalUserId If this is true, then this will return the approval set for this category
		*
		* @return arary An array of names to signify the trail to the category
		*/
		function GetCatPathArray($catid, $showApprovalUserId = false)
		{
			$catPath = array();
			$this->getCatsInfo();

			$catids = $this->tree->GetBranchTo($catid);
			foreach ($catids as $catid) {
				if ($showApprovalUserId) {
					$catPath[] = array($catid, $this->catsById[$catid]['name'], $this->catsById[$catid]['approvaltype']);
				} else {
					$catPath[] = array($catid, $this->catsById[$catid]['name']);
				}
			}

			return array_reverse($catPath);
		}

		/**
		* _GetQuestionCount
		* Get the details of number of questions for a category id
		*
		* @param array $qids An array of question ids to get the associations for
		*
		* @return void
		*/
		function _GetQuestionCount($categoryid)
		{
			if (empty($this->questionCount)) {
				$query = "SELECT categoryid, count(questionid) as num
					FROM ".$GLOBALS['tablePrefix']."categoryassociations
					GROUP BY categoryid";
				$result = $GLOBALS['AKB_DB']->Query($query);
				while ($row = $GLOBALS['AKB_DB']->Fetch($result)) {
					$this->questionCount[$row['categoryid']] = $row['num'];
				}
			}

			if (isset($this->questionCount[$categoryid])) {
				return $this->questionCount[$categoryid];
			} else {
				return 0;
			}
		}

		/**
		* _GetSubCount
		* Returns the number of sub categories under a given categoryid
		*
		* @param int $catid The catid to get the number of subcats for
		*
		* @return int The number of sub categories of $catid
		*/
		function _GetSubCount($catid)
		{
			$this->getCatsInfo();
			return $this->tree->GetNodeCount($catid);
		}

		/**
		* getCatsSortable
		* Returns an array of categories, each indented and prefixed depending
		* on it's position in the category structure. This function calls itself
		* recursively.
		*
		* @param string $stalk What to prefix a question with to signify it is
		* a descendant of its parent
		* @param int $parentid The category id to get descdendants of
		* @param string $prefix This string grows with whitespace depending on
		* the depth of the item in the tree
		*
		* @return array The formatted array of categories
		*/
		function getCatsSortable($stalk = '_{-}_', $parentid=0, $prefix='')
		{
			$subs = array();
			$formatted = array();

			// If we don't have any data get it from the db
			$this->getCatsInfo();
			if (empty($this->tree->nodesByPid)) {
				$parentid = 0;
			}

			if (!isset($this->tree->nodesByPid[$parentid])) {
				return $formatted;
			}

			// Create the formatted array
			foreach ($this->tree->nodesByPid[$parentid] as $k => $catid) {
				$cat = $this->catsById[$catid];
				if (!empty($prefix)) {
					$formatted[$cat['categoryid']] = $prefix.$stalk.htmlspecialchars($cat['name'], ENT_QUOTES, $GLOBALS['charset']);
				} else {
					$formatted[$cat['categoryid']] = $prefix.htmlspecialchars($cat['name'], ENT_QUOTES, $GLOBALS['charset']);
				}
				$subs = $this->getCatsSortable($stalk, $cat['categoryid'],$prefix.'_{-}_');
				$formatted = $formatted + $subs;
			}

			return $formatted;
		}

		/**
		* ShowCategoryList
		* Display the list of categories for the manage categories page
		*
		* @param string $MsgDesc The text of any message for the user
		* @param int $MsgStatus The status of the message
		*
		* @return void
		*/
		function ShowCategoryList($MsgDesc = '', $MsgStatus = '')
		{
			// Check they have permission to be here
			if (!$_SESSION['user']->verify('category', 'edit')
			&& !$_SESSION['user']->verify('category', 'delete')) {
				$this->NoPermission();
				return;
			}

			// Show a list of users to edit/delete
			$GLOBALS['CategoryGrid'] = '';

			if ($_SESSION['user']->verify('category', 'edit')) {
				$GLOBALS['ShowCreateButton'] = 'inline';
			} else {
				$GLOBALS['ShowCreateButton'] = 'none';
			}

			if ($_SESSION['user']->verify('category', 'delete')) {
				$GLOBALS['ShowDeleteButton'] = 'inline';
			} else {
				$GLOBALS['ShowDeleteButton'] = 'none';
			}

			if (isset($_GET['d'])) {
				$MsgDesc = GetLang('CategoryDeleted');
				$MsgStatus = MSG_SUCCESS;
			}

			if ($MsgDesc != '') {
				$GLOBALS['Message'] = MessageBox($MsgDesc, $MsgStatus);
			}

			$this->getCatsInfo();

			$GLOBALS['BreadcrumEntries'][GetLang("UserManageCategories")] = "";

			$permitted = $this->GetPermittedCats();

			$cats = $this->getCatsSortable();


			if (!empty($permitted)) {
				$cats_to_keep = $permitted;
				foreach ($permitted as $catid) {
					if (is_array($cats_to_keep) && is_array($this->tree->GetBranchTo((int) $catid))) {
						$cats_to_keep = array_merge($cats_to_keep, $this->tree->GetBranchTo((int) $catid));
					}
				}
			} else {
				// There are no restrictions on which cats we can see
				$cats_to_keep = array_keys($cats);
			}

			if (!empty($cats) && !empty($cats_to_keep)) {
				$currCount = -2;
				$prevCount = -2;
				foreach ($cats as $catid => $catname) {

					if (!in_array($catid, $cats_to_keep)) {
						continue;
					}

					if (!$currCount = strrpos($catname, '_{-}_')) {
						$currCount = -1;
					}

					$catname = str_replace('_{-}_', '', $catname);
					$GLOBALS['StartCatRow'] = '';
					$GLOBALS['EndCatRow'] = '';
					if ($currCount > $prevCount) {
						$GLOBALS['StartCatRow'] = ' <ul class="SortableList"> ';
						if ($prevCount == -2) {
							$GLOBALS['StartCatRow'] = ' <ul class="SortableList" id="CategoryList"> ';
						}
					} else if ($currCount < $prevCount) {
						$GLOBALS['StartCatRow'] = '';
						for ($j = $currCount+5; $j <= $prevCount; $j+=5) {
							$GLOBALS['StartCatRow'] .= ' </ul> ';
						}
					}
					$GLOBALS['StartCatRow'] .= ' <li id="ele-'.$catid.'" class="SortableRow"> ';

					$GLOBALS['CatId'] 		= $catid;
					$GLOBALS['CatIcon'] 	= $this->catsById[$catid]['icon'];
					$GLOBALS['CatName'] 	= $catname;

					$GLOBALS['SubCats'] 	= $this->_GetSubCount($catid);
					$GLOBALS['Questions'] 	= $this->_GetQuestionCount($catid);
					$GLOBALS['Security'] 	= ucwords($this->catsById[$catid]['security']);
					if (GetLang('CatSec'.strtolower($this->catsById[$catid]['security']))) {
						$GLOBALS['Security'] 	= ucwords(GetLang('CatSec'.strtolower($this->catsById[$catid]['security'])));
					}

					$GLOBALS['securitycss']	= "security_".$this->catsById[$catid]['security'];

					if ($GLOBALS['CatName'] != '') {
						//if (empty($permitted) || in_array($catid, $permitted)) {
						if ($_SESSION['user']->verify('category', 'edit') && (empty($permitted) || in_array($catid, $permitted))) {
							$GLOBALS['AKB_CLASS_TEMPLATE']->SetTemplate('category.row');
						} else {
							$GLOBALS['AKB_CLASS_TEMPLATE']->SetTemplate('category.restricted.row');
						}
						$GLOBALS['CategoryGrid'] .= $GLOBALS['AKB_CLASS_TEMPLATE']->ParseTemplate(true);
					}
					$prevCount = $currCount;
				}
				$GLOBALS['CategoryGrid'] .= '</ul>';
				$GLOBALS['FormAction'] = 'index.php?ToDo=viewCategories';
				$GLOBALS["AKB_CLASS_TEMPLATE"]->SetTemplate("category.manage");
				$GLOBALS["AKB_CLASS_TEMPLATE"]->ParseTemplate();
			} else {
				// There aren't any questions, show a message so they can create some
				$GLOBALS['Style'] = '';
				$GLOBALS['Title'] = GetLang('ManageCategories');
				$GLOBALS['ButtonText'] = GetLang('CreateCategory');
				$GLOBALS['ButtonClass'] = 'Field150';
				$GLOBALS['Message'] = MessageBox(GetLang('NoCategories'), MSG_SUCCESS);
				$GLOBALS['URL'] = 'index.php?ToDo=createCategory';

				$GLOBALS['AKB_CLASS_TEMPLATE']->SetTemplate('messagewithbutton');
				$GLOBALS['AKB_CLASS_TEMPLATE']->ParseTemplate();
			}
		}

		/**
		* ShowNewCategory
		* Show the create new category page
		*
		* @return void
		*/
		function ShowNewCategory()
		{
			// Check they have permission to be here
			if (!$_SESSION['user']->verify('category', 'edit')) {
				$this->NoPermission();
				return;
			}

			// Create a new category
			// First up, get form variables if there's an error

			$name = "";
			$pass = "";
			$cat = array();
			$GLOBALS['hiddenFields'] = '';
			$GLOBALS['CategoryDesc'] = '';
			$GLOBALS['CategoryOrder'] = 0;
			$GLOBALS['GroupApprovalOptions'] = $this->_GetGroupsAsOptions(0);

			if (isset($_GET["parentId"])) {
				$cat[] = $_GET["parentId"];
			}

			//Setup Breadcrumbs
			$GLOBALS['BreadcrumEntries'][GetLang("UserManageCategories")] = "index.php?ToDo=viewCategories";
			$GLOBALS['BreadcrumEntries'][GetLang("CreateCategories")] = "";

			//Setup groups
			$GLOBALS['approvaltypechecked'] = 0;
			$GLOBALS['groups'] = $this->getGroupsForSelect(true);
			$GLOBALS['groupListSize'] = 10;
			$GLOBALS['HasWfGroups'] = 0;

			$GLOBALS["FormAction"] = "saveNewCategory";
			$GLOBALS["CatTitle"] = GetLang("AddCatTitle");
			$GLOBALS["CatIntro"] = GetLang("AddCatIntro");
			$GLOBALS["CategoryIcon"] = "1.gif";
			$GLOBALS["CategoryName"] = $name;
			$GLOBALS["CategoryPassword"] = $pass;

			// If there are category restrictions on this user, don't show the
			// "No Parent Category" item in the options list
			$applies = $this->GetApplies();
			if (empty($applies)) {
				$GLOBALS["CategoryOptions"] = $this->GetCategoryOptions($cat);
			} else {
				$GLOBALS["CategoryOptions"] = $this->GetCategoryOptions($cat, $Container = "<option %s value='%d'>%s</option>", $Sel = "SELECTED", $Divider = "- ", false);
			}

			$GLOBALS["CategoryIcons"] = $this->GetIconList();

			$GLOBALS['lblRestrictPublic'] = GetLang("RestrictPublicCat");
			$GLOBALS['lblRestrictPrivate'] = GetLang("RestrictPrivateCat");

			if (!validUrl()) {
				$GLOBALS['HidePanels'][] = 'UserGroupsWorkflow';
			}

			$GLOBALS["AKB_CLASS_TEMPLATE"]->SetTemplate("category.form");
			$GLOBALS["AKB_CLASS_TEMPLATE"]->ParseTemplate();
		}

		/**
		* SaveNewCategory
		* Save a new category to the database
		*
		* @return void
		*/
		function SaveNewCategory()
		{
			// Check they have permission to be here
			if (!$_SESSION['user']->verify('category', 'edit')) {
				$this->NoPermission();
				return;
			}

			if (!isset($_POST['approvaltype'])) {
				$_POST['approvaltype'] = CATEGORIES_NO_APPROVAL;
			}

			$category = new API_CATEGORY();

			$approvalGroupString = 'groups' . $_POST['approvaltype'];

			if (isset($_POST[$approvalGroupString]) && is_array($_POST[$approvalGroupString])) {
				if (isset($_POST['groups']) && is_array($_POST['groups'])) {
					$_POST['groups'] = array_merge($_POST[$approvalGroupString], $_POST['groups']);
				} else {
					$_POST['groups'] = $_POST[$approvalGroupString];
				}
				$_POST['groups'] = array_unique($_POST['groups']);
			}

			foreach ($_POST['category'] as $catid) {
				$_POST['parentid'] = (int) $catid;

				if ($_POST['restrict'] == 0) {
					$_POST['security'] = "public";
				} else {
					$_POST['security'] = "private";
				}

				if ($new_categoryid = $category->create()) {
					$this->saveGroupsForCategory($new_categoryid);
					$msg = sprintf(GetLang("CategoryCreatedSuccessfully"), htmlspecialchars($_POST['name'], ENT_QUOTES, $GLOBALS['charset']));
					$status = MSG_SUCCESS;
					$_SESSION['user']->loadPerms();
				} else {
					$msg = sprintf(GetLang("CategoryCreatedFailed"), htmlspecialchars($_POST['name'], ENT_QUOTES, $GLOBALS['charset']));
					$msg .= ' '.$category->error;
					$status = MSG_ERROR;
					break;
				}
			}
			$this->ShowCategoryList($msg,$status);
		}

		/**
		* DeleteCategory
		* Delete a category and all its children from the database
		*
		* @return void
		*/
		function DeleteCategory()
		{
			// Check they have permission to be here
			if (!$_SESSION['user']->verify('category', 'delete')) {
				$this->NoPermission();
				return;
			}

			$arrDel = array();
			$i = 0;

			if (isset($_GET["catId"])) {
				$catId = $_GET["catId"];

				$arrCats = $GLOBALS["AKB_CLASS_CATEGORY"]->GetCategoryOptions(array(), "%s %s|~~ %s\n" , "", "", false);
				$arrCats1 = explode("\n", $arrCats);

				foreach ($arrCats1 as $c) {
					$arrCatList[] = explode("|~~", trim($c));
				}

				foreach ($arrCatList as $c) {
					if ($c[0] == $catId) {
						$tmp = str_replace("&nbsp;", "```", $c[1]);
						$arrTmp = explode("```", $tmp);
						$spaces = sizeof($arrTmp) - 1;

						$arrDel[] = $c[0];

						// Work our way down the list and delete categories whose
						// number of spaces don't match this entries
						for ($j = $i; $j < sizeof($arrCatList); $j++) {
							if ($j > $i) {
								if (isset($arrCatList[$j][1])) {
									$tmp = str_replace("&nbsp;", "```", $arrCatList[$j][1]);
									$arrTmp = explode("```", $tmp);
									$spaces1 = sizeof($arrTmp) - 1;

									if ($spaces1 > $spaces) {
										$arrDel[] = $arrCatList[$j][0];
									} else {
										break;
									}
								}
							}
						}
						break;
					}
					$i++;
				}

				foreach ($arrDel as $c) {
					$query = sprintf("delete from %scategories where categoryid='%d'", $GLOBALS["tablePrefix"], $c);
					$GLOBALS['AKB_DB']->Query($query);
				}

				ob_end_clean();
				header("Location: index.php?ToDo=viewCategories&d=1");
				die();
			} else {
				$this->ShowCategoryList();
			}
		}

		/**
		* EditCategory
		* Show the edit a category page
		*
		*@return void
		*/
		function EditCategory()
		{
			// Check they have permission to be here
			if (!$_SESSION['user']->verify('category', 'edit')) {
				$this->NoPermission();
				return;
			}

			if (isset($_GET["catId"])) {
				$catId = (int) $_GET["catId"];

				$GLOBALS['BreadcrumEntries'][GetLang("UserManageCategories")] = "index.php?ToDo=viewCategories";
				$GLOBALS['BreadcrumEntries'][GetLang("UserEditCategories")] = "";

				// Check if they have permission to be editing this category
				// and boot them if they don't
				$permitted = $this->GetPermittedCats();
				$applies = $this->GetApplies();
				if (!empty($applies) && !in_array($catId, $permitted)) {
					$this->ShowCategoryList();
					return;
				}

				$query = sprintf("select * from %scategories where categoryid='%d'", $GLOBALS["tablePrefix"], $catId);
				$catResult = $GLOBALS['AKB_DB']->Query($query);

				if ($catRow = $GLOBALS['AKB_DB']->Fetch($catResult)) {
					$name = $catRow["name"];
					$desc = $catRow["description"];
					$pass = $catRow["pass"];
					$catorder = $catRow["catorder"];
					$cat = array($catRow["parentid"]);
					$icon = $catRow["icon"];
					$security = $catRow["security"];
					$GLOBALS['approvaltypechecked'] = $catRow["approvaltype"];

					//Setup groups
					$GLOBALS['groups'] = $this->getGroupsForSelect(true);
					$GLOBALS['groupListSize'] = 10;

					if ($security == "public") {
						$GLOBALS['HasGroups'] = 0;
					} else {
						$GLOBALS['HasGroups'] = 1;
					}



					// Does the category have a user set for approval?
					$GLOBALS['GroupApprovalOptions'] = $this->_GetGroupsAsOptions($catId);
					$GLOBALS["FormAction"] = "saveUpdatedCategory";
					$GLOBALS["hiddenFields"] = sprintf("<input type='hidden' name='categoryid' value='%d'>", $catId);
					$GLOBALS["CatTitle"] = GetLang("UpdateCatTitle");
					$GLOBALS["CatIntro"] = GetLang("UpdateCatIntro");
					$GLOBALS["CategoryName"] = htmlspecialchars($name, ENT_QUOTES, $GLOBALS['charset']);
					$GLOBALS["CategoryDesc"] = htmlspecialchars($desc, ENT_QUOTES, $GLOBALS['charset']);
					$GLOBALS["CategoryPassword"] = $pass;
					$GLOBALS["CategoryOrder"] = $catorder;

					$GLOBALS["CategoryOptions"] = $this->GetParentSelectOptions($catId);
					$GLOBALS["CategoryIcons"] = $this->GetIconList($icon);
					$GLOBALS["CategoryIcon"] = $icon;

					$GLOBALS['lblRestrictPublic'] = GetLang("RestrictPublicCat");
					$GLOBALS['lblRestrictPrivate'] = GetLang("RestrictPrivateCat");

					if (!validUrl()) {
						$GLOBALS['HidePanels'][] = 'UserGroupsWorkflow';
					}

					$GLOBALS["AKB_CLASS_TEMPLATE"]->SetTemplate("category.form");
					$GLOBALS["AKB_CLASS_TEMPLATE"]->ParseTemplate();
				} else {
					$this->ShowCategoryList();
				}
			} else {
				$this->ShowCategoryList();
			}
		}

		/**
		* SaveUpdatedCategory
		* Save updates of a category back to the database
		*
		* @return void
		*/
		function SaveUpdatedCategory()
		{
			// Check they have permission to be here
			if (!$_SESSION['user']->verify('category', 'edit')) {
				$this->NoPermission();
				return;
			}

			if (!isset($_POST['categoryid'])) {
				$this->ShowCategoryList();
				return;
			}

			if (!isset($_POST['approvaltype'])) {
				$_POST['approvaltype'] = CATEGORIES_NO_APPROVAL;
			}

			// Check if they have permission to be editing this category
			// and boot them if they don't
			$permitted = $this->GetPermittedCats();
			$applies = $this->GetApplies();
			if (!empty($applies) && !in_array($_POST['categoryid'], $permitted)) {
				$this->ShowCategoryList();
				return;
			}

			$category = new API_CATEGORY();
			$category->load($_POST['categoryid']);
			$failure = true;

			$approvalGroupString = 'groups' . $_POST['approvaltype'];

			if (isset($_POST[$approvalGroupString]) && is_array($_POST[$approvalGroupString])) {
				if (isset($_POST['groups']) && is_array($_POST['groups'])) {
					$_POST['groups'] = array_merge($_POST[$approvalGroupString], $_POST['groups']);
				} else {
					$_POST['groups'] = $_POST[$approvalGroupString];
				}
				$_POST['groups'] = array_unique($_POST['groups']);
			}

			$this->saveGroupsForCategory();

			if ($_POST['restrict'] == 0) {
				$_POST['security'] = "public";
			} else {
				$_POST['security'] = "private";
			}

			foreach ($_POST['category'] as $catid) {
				$_POST['parentid'] = (int) $catid;
				$failure = $failure && $category->save();
			}

			if ($failure) {
				$this->catsCached = false;
				$_SESSION['user']->loadPerms();
				$this->ShowCategoryList(sprintf(GetLang("CategoryUpdateSuccess"), htmlspecialchars($_POST['name'], ENT_QUOTES, $GLOBALS['charset'])), MSG_SUCCESS);
			} else {
				$msg = sprintf(GetLang("CategoryUpdateFail"), htmlspecialchars($_POST['name'], ENT_QUOTES, $GLOBALS['charset']));
				$msg .= $category->error;
				$this->ShowCategoryList($msg, MSG_ERROR);
			}
		}

		/**
		* GetQuestionCategories
		* Get the categories for a given question
		*
		* @param int $qid The question id to get the categories for
		*
		* @return string the names of the categories
		*/
		function GetQuestionCategories($qid)
		{
			if (is_array($qid)) {
				$this->getAssocInfo($qid);
			} else {
				$this->getAssocInfo(array());
			}

			$this->getCatsInfo();

			// Get a list of categories that a question appears in and return them
			$catNames = "";
			$first = true;

			if (isset($this->assocsByQ[$qid]) && !empty($this->assocsByQ[$qid])) {
				foreach ($this->assocsByQ[$qid] as $catid) {
					// If the category is set add its name to the list
					if (isset($this->catsById[$catid])) {
						$cat = $this->catsById[$catid];
						if ($first) {
							$first = false;
							$catNames .= $cat['name'];
						} else {
							$catNames .= ", " . $cat['name'];
						}
					}
				}
			}

			return (trim($catNames) == '') ? GetLang("NA") : $catNames;
		}

		/**
		* GetIconList
		* Get a list of category icons and output them
		*
		* @param string $SelectIcon the filename of the selected icon
		*
		* @return string The code to display the images
		*/
		function GetIconList($SelectedIcon = "")
		{
			$i = 0;
			$output = "";
			$catImagePath = dirname(__FILE__).'/../../../templates/' . $GLOBALS['template'] . '/Images/Category';
			$catImageUrlDir = $GLOBALS['pathToKB'] . '/templates/'. $GLOBALS['template'] .'/Images/Category';

			if ($SelectedIcon == "") {
				$SelectedIcon = "1.gif";
			}
			if ($dh = @opendir($catImagePath)) {
				while ($icon = @readdir($dh)) {
					if ($icon != "." && $icon != "..") {
						if (strtolower($icon) == $SelectedIcon) {
							$sel = "class='SelectedCategoryIcon'";
						} else {
							$sel = "class='CategoryIcon'";
						}

						$parts = explode('.', $icon);
						$ext = array_pop($parts);

						$valid_extensions = array ('gif', 'png');

						if (in_array($ext, $valid_extensions)) {
							$size = getimagesize($catImagePath.'/'.$icon);
							$output .= sprintf("<span id=\"span_cat_%d\" onClick=\"SelectIcon(%d, '%s')\" %s><img id=\"icon_cat_%d\" src=\"%s/%s\" alt=\"%s\" /></span>", $i, $i, $icon, $sel, $i, $catImageUrlDir, $icon, $icon);
							$i++;
						}
					}
				}
				$GLOBALS["IconCount"] = $i;
				return $output;
			} else {
				return sprintf("<img src='images/error.gif' align='left' hspace='7'> %s", GetLang("CatIconError"));
			}
		}

		/**
		* GetApplies
		* Get a list of categories that the permissions of a group apply to
		*
		* @return array
		*/
		function GetApplies()
		{
			$applies = array();

			if (is_array($_SESSION['user']->groups) && !empty($_SESSION['user']->groups)) {
				foreach ($_SESSION['user']->groups as $group) {
					if (isset($group->applies['category']) && is_array($group->applies['category'])) {
						$applies = array_merge($applies, $group->applies['category']);
					} else if (isset($group->applies['category']) && trim(strtolower($group->applies['category'])) == "all") {
						//Special case for when the "all" keyword is used. Need to get all of the categories
						//and return them. Then we can break out of the loop because this user obviously belongs to
						//a group that has access to everything.
						$applies = $this->tree->GetTreeDepthFirst();
						break;
					}
				}
			}
			return $applies;
		}

		/**
		* GetApprovalApplies
		* Get a list of categories that the approval permissions of a group apply to
		*
		* @return array
		*/
		function GetApprovalApplies()
		{
			$applies = array();
			if (is_array($_SESSION['user']->groups) && !empty($_SESSION['user']->groups)) {
				foreach ($_SESSION['user']->groups as $group) {
					if (isset($group->applies['category']) && is_array($group->applies['category'])
					&& (isset($group->perms['category']['approve']) && $group->perms['category']['approve'])
					) {

						$applies = array_merge($applies, $group->applies['category']);
					} else if ((isset($group->perms['category']['approve']) && $group->perms['category']['approve']) && (isset($group->applies['category']) && $group->applies['category'] == 'all')) {
						return $applies = $this->tree->GetTreeDepthFirst();
					}
				}
			}
			return $applies;
		}

		/**
		* GetPermittedCats
		* Get an array of permitted category ids that the currently logged in
		* user is allowed to access taking into account where the category is
		* in the tree structure
		*
		* @return array
		*/
		function GetPermittedApprovalCats()
		{
			$this->getCatsInfo();
			$applies = $this->GetApprovalApplies();
			$permitted = array();
			if (!empty($applies)) {
				foreach ($applies as $catid) {
					$permitted[] = $catid;
				}
			}
			return $permitted;
		}
		/**
		* GetPermittedCats
		* Get an array of permitted category ids that the currently logged in
		* user is allowed to access taking into account where the category is
		* in the tree structure
		*
		* @return array
		*/
		function GetPermittedCats()
		{
			$applies = $this->GetApplies();
			$permitted = array();

			$this->getCatsInfo();

			if (!empty($applies)) {
				foreach ($applies as $catid) {
					$permitted[] = $catid;
					$permitted = array_merge($permitted, $this->tree->GetBranchFrom($catid, false));
				}
			} else {
				$permitted = $this->tree->GetTreeDepthFirst();
			}
			return $permitted;
		}

		/**
		* getGroupsForSelect
		* Get all the applicable groups for this category
		*
		* @return array
		*/
		function getGroupsForSelect($nonApprovalGroups = false) {
			//Get the groups
			$group = new API_GROUP();
			$groups = $group->getList();

			//Because of the way categories per group are stored, we need to loop through each one
			//to get the right ones to select.
			$selectedGroupsArray = array();
			$nonApprovalGroupsArray = array();

			$categoryId = (int)@$_GET['catId'];

			$SQL = "
				SELECT groupid,application,permissions
				FROM
					".$GLOBALS['tablePrefix']."groups
			";

			$result = $GLOBALS['AKB_DB']->Query($SQL);

			while ($rstemp = $GLOBALS['AKB_DB']->Fetch($result)) {
				$tempArr = unserialize(str_replace('\"', '"', $rstemp['application']));
				$tempPerm = unserialize(str_replace('\"', '"', $rstemp['permissions']));
				if ($categoryId > 0) {
					if (isset($tempArr['category'])) {
						if (($tempArr['category'] == "all") || (in_array($categoryId,$tempArr['category']))) {
							$selectedGroupsArray[] = $rstemp['groupid'];
						}
					}
				}
				if ($nonApprovalGroups && (!isset($tempPerm['category']['approve']) || (!$tempPerm['category']['approve'] || ($tempPerm['category']['approve'] && isset($tempArr['feonly']) && $tempArr['feonly'] == '1')))) {
					$nonApprovalGroupsArray[] = $rstemp['groupid'];
				}
			}

			//For the private/public radio button.
			$GLOBALS['HasGroups'] = (int)$selectedGroupsArray;

			$groupsHTML = "";

			foreach ($groups as $id => $name) {
				if (in_array($id, $nonApprovalGroupsArray)) {
					$groupsHTML .= '<option value="'.$id.'"';
					$groupsHTML .= (in_array($id, $selectedGroupsArray)) ? ' selected="selected" ' : '';
					$groupsHTML .= '>'. htmlspecialchars($name, ENT_QUOTES, $GLOBALS['charset']);
					$groupsHTML .= "</option>\n";
				}
			}

			return $groupsHTML;
		}

		/**
		* saveGroupsForCategory
		* Takes the post array and makes sure that this category is saved in each
		* group that is selected.
		*
		* We dont do anything unless we have to, so we compare the original saved values before we delve
		* in, otherwise it could get a bit messy.
		*
		* Returns an array of category ids to set the security for.
		*
		* @return array
		*/
		function saveGroupsForCategory($passedCat=false) {
			if (!isset($_POST)) {
				return false;
			}

			if (!$passedCat) {
				$_catId = (int)$_POST['categoryid'];
			} else {
				$_catId = (int)$passedCat;
			}

			//Now we loop through all the groups in the system and see what has changed.
			$groups = new API_GROUP();
			$glist = $groups->getList(true);

			$postTemp = $_POST;
			$_POST = array();

			//Whatever we do we need to do it for an entire branch.
			if (isset($GLOBALS['categoryStatusOverwrite']) && $GLOBALS['categoryStatusOverwrite']) {
				$branchCats = $this->tree->GetBranchFrom($_catId,false);
			}
			$branchCats[] = $_catId;
			foreach ($branchCats as $categoryId) {
				if ($postTemp['restrict'] == 1 || $postTemp['approvaltype'] > 0) {
					foreach ($glist as $groupid=>$val) {
						//There are restrictions. We see what is selected.
						$group = new API_GROUP();
						$group->load($groupid);

						if ((isset($postTemp['groups'])) && (is_array($postTemp['groups']))) {
							if (in_array($groupid,$postTemp['groups'])) {
								//This $groupid is selected therefore we need to alter the serialized array to reflect as such.
								if (($group->applies['category'] != "all") && (!in_array($categoryId,$group->applies['category']))) {
									$group->applies['category'][] = $categoryId;
									$group->save();
								}
							} else {
								if ($postTemp['restrict'] == 1 || ($postTemp['approvaltype'] > 0 && isset($group->perms['category']['approve']) && $group->perms['category']['approve'] == '1')) {
									//This group is not selected. Need to get rid of it.
									if ($group->applies['category'] == "all") {
										//Going from all to minus this category. Need to add every single category except this one to the serialized array for that group.
										$group->applies['category'] = $this->GetPermittedCats();
									}

									//Loop through and remove the current category
									$buildArray = array();
									foreach ($group->applies['category'] as $catId) {
										if ($catId != $categoryId) {
											$buildArray[] = $catId;
										}
									}

									$group->applies['category'] = $buildArray;
									$group->save();
								}
							}
						}
					}
				}

				if ($postTemp['restrict'] == 0) {
					$this->setCategorySecurity($categoryId,"public");
				} else {
					$this->setCategorySecurity($categoryId,"private");
				}
			}

			$_POST = $postTemp;
		}

		function setCategorySecurity($categoryid,$security) {
			$SQL = "
				UPDATE ".$GLOBALS['tablePrefix']."categories
				SET
					security = '".$GLOBALS['AKB_DB']->Quote($security)."'
				WHERE
					categoryid = '".(int)$categoryid."'
			";

			return $GLOBALS['AKB_DB']->Query($SQL);
		}

		/**
		* _GetUsersAsOptions
		* Get a list of users as option tags
		*
		* @param Int $Selected The currently selected user (used when updating a category)
		* @return String The list of option tags
		*/
		function _GetUsersAsOptions($Selected=0)
		{
			require(CLASS_DIR.'/class.user.php');
			$user_array = AKB_USER::_GetUserListAsArray();
			$options = '';
			$sel = '';

			foreach($user_array as $user) {
				if($Selected == $user['userid']) {
					$sel = 'SELECTED';
				}
				else {
					$sel = '';
				}
				$options .= sprintf('<option value="%d" %s>%s</option>', $user['userid'], $sel, $user['username']);
			}

			return $options;
		}

		/**
		* _GetGroupsAsOptions
		* Get a list of users as option tags
		*
		* @param Int $Selected The currently selected user (used when updating a category)
		* @return String The list of option tags
		*/
		function _GetGroupsAsOptions($catId)
		{
			$query = sprintf('SELECT groupid, name FROM %sgroups ORDER BY name ASC', $GLOBALS['tablePrefix']);
			$result = $GLOBALS['AKB_DB']->Query($query);
			$groups = array();
			$options = '';
			$sel = '';

			while($row = $GLOBALS['AKB_DB']->Fetch($result)) {
				$group = new API_GROUP();
				$groupid = $row['groupid'];
				$group->load($groupid);
				$groupPerm = $group->perms;
				if (isset($groupPerm['question']['edit']) && $groupPerm['question']['edit'] == '1'
				&& isset($groupPerm['category']['approve']) && $groupPerm['category']['approve'] == '1'
				&& (isset($group->applies['feonly']) && $group->applies['feonly'] != '1')) {
					if (is_array($group->applies['category']) && in_array($catId, $group->applies['category'])) {
						$sel = ' SELECTED ';
					} else if ($group->applies['category'] == 'all') {
						$sel = ' SELECTED ';
					} else {
						$sel = '';
					}
					$options .= sprintf('<option value="%d" %s>%s</option>', $groupid, $sel, $row['name']);
				}
			}
			return $options;
		}

		/**
		* GetApprovalGroups
		*
		* @param Array $catIds The array of a list of target category ids
		*
		* @return Array An Array of the groups which has approval permission to the parsed in $catIds
		*
		*/
		function GetApprovalGroups($catIds)
		{
			$validGroups = array();
			if (!is_array($catIds) || !sizeof($catIds)) {
				return $validGroups;
			}
			// Get all the available groups
			$query = sprintf('SELECT groupid FROM %sgroups ORDER BY name ASC', $GLOBALS['tablePrefix']);
			$result = $GLOBALS['AKB_DB']->Query($query);
			while($row = $GLOBALS['AKB_DB']->Fetch($result)) {
				$group = new API_GROUP();
				$groupid = $row['groupid'];
				$group->load($groupid);

				$groupPerm = $group->perms;
				if ((isset($groupPerm['question']['edit']) && $groupPerm['question']['edit'] == '1'
				&& isset($groupPerm['category']['approve']) && $groupPerm['category']['approve'] == '1') && $group->applies['feonly'] != '1'
				) {
					if ((is_array($group->applies['category']) &&
					array_intersect($catIds, $group->applies['category'])) || $group->applies['category'] == 'all') {
						if (is_array($group->applies['category'])) {
							$intersectGroupCat = array_intersect($catIds, $group->applies['category']);
						} else {
							$intersectGroupCat = $catIds;
						}
						$validGroups[] = $groupid;
					}
				}
			}
			return $validGroups;
		}

	}

?>